package cn.mw.cmdb.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.mw.cmdb.entity.InstanceInfo;
import cn.mw.cmdb.param.ParamContext;
import cn.mw.cmdb.param.QueryMonitorParam;
import cn.mw.cmdb.param.QueryMonitorValue;
import cn.mw.cmdb.param.ZabbixProcessorParam;
import cn.mw.cmdb.processor.MonitorParamContext;
import cn.mw.cmdb.processor.MonitorProcessor;
import cn.mw.main.api.common.ZabbixDubboService;
import cn.mw.microMonitorCommon.entity.common.MwTPServerTable;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static cn.mw.cmdb.util.ValConvertUtil.intValueConvert;


@Slf4j
public class MwInstanceMonitorManage {

    private ZabbixDubboService zabbixDubboService;

    private CmdbServiceManage cmdbServiceManage;

    public MwInstanceMonitorManage(ZabbixDubboService zabbixDubboService, CmdbServiceManage cmdbServiceManage) {
        this.zabbixDubboService = zabbixDubboService;
        this.cmdbServiceManage = cmdbServiceManage;
    }

    public MwInstanceMonitorManage(ZabbixDubboService zabbixDubboService) {
        this.zabbixDubboService = zabbixDubboService;
    }

    public QueryMonitorParam zabbixParamConvert(String instanceId) {
        InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
        InstanceInfo instanceParam = new InstanceInfo();
        instanceParam.setId(instanceId);
        InstanceInfo instanceInfo = instanceService.doSelectById(instanceParam);
        QueryMonitorParam queryMonitorParam = new QueryMonitorParam();
        //判断该实例信息是否符合规范，存在MonitorType、MonitorServerId、MonitorTypeId
        if (instanceInfo != null && !Strings.isNullOrEmpty(instanceInfo.getMonitorType()) && intValueConvert(instanceInfo.getMonitorServerId()) != 0
                && !Strings.isNullOrEmpty(instanceInfo.getMonitorTypeId())) {
            Integer monitorServerId = instanceInfo.getMonitorServerId();
            String hostId = instanceInfo.getMonitorTypeId();
            queryMonitorParam.setMonitorServerId(monitorServerId);
            queryMonitorParam.setHostIds(Arrays.asList(hostId));
            queryMonitorParam.setMonitorType(instanceInfo.getMonitorType());
        }
        return queryMonitorParam;
    }

    /**
     * 根据不同的serverId对数据进行分组
     *
     * @param instanceInfos
     * @return
     */
    public List<QueryMonitorValue> zabbixParamListConvert(Object instanceInfos) {
        List<InstanceInfo> instanceInfoList = (List<InstanceInfo>) instanceInfos;
        List<QueryMonitorValue> list = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(instanceInfoList)) {
            //根据MonitorType区分，过滤保留serverId，hostId有值的实例数据
            List<InstanceInfo> disList = instanceInfoList.stream().filter(s -> !Strings.isNullOrEmpty(s.getMonitorType())
                    && intValueConvert(s.getMonitorServerId()) != 0
                    && !Strings.isNullOrEmpty(s.getMonitorTypeId())).collect(Collectors.toList());

            Map<Integer, String> monitorTypeMap = disList.stream().collect(Collectors.toMap(s -> s.getMonitorServerId(), s -> s.getMonitorType(), (
                    value1, value2) -> {
                return value2;
            }));
            Map<Integer, List<String>> hostInfoMap = disList.stream().collect(Collectors.groupingBy(s -> s.getMonitorServerId(), Collectors.mapping(s -> s.getMonitorTypeId(), Collectors.toList())));
            for (Map.Entry<Integer, List<String>> entry : hostInfoMap.entrySet()) {
                Integer k = entry.getKey();
                List<String> v = entry.getValue();
                QueryMonitorValue queryMonitorParam = new QueryMonitorParam();
                queryMonitorParam.setMonitorServerId(k);
                queryMonitorParam.setHostIds(v);
                if (monitorTypeMap != null && monitorTypeMap.containsKey(k)) {
                    queryMonitorParam.setMonitorType(monitorTypeMap.get(k));
                }
                list.add(queryMonitorParam);
            }
        }
        return list;
    }

    /**
     * 多监控服务情况下
     * 使用多线程处理指定监控方法
     *
     * @param zabbixParams
     * @param methodName   方法名称
     * @return
     * @throws Exception
     */
    public List<Object> handlerMonitorMethodThread(List<QueryMonitorValue> zabbixParams,Object param, String methodName) throws Exception {
        List<Object> listInfo = new ArrayList();
        if (CollectionUtils.isNotEmpty(zabbixParams)) {
            //反射获取MonitorProcessor对象
            Class<MonitorProcessor> monitorProcessorClass = MonitorProcessor.class;
            //设置指定方法的参数对象
            Class<?>[] parameterType = {MonitorParamContext.class};
            //获取方法
            Method method = monitorProcessorClass.getMethod(methodName, parameterType);
            //使用线程处理多个server服务
            int coreSizePool = Runtime.getRuntime().availableProcessors() * 2 + 1;
            coreSizePool = (coreSizePool < zabbixParams.size()) ? coreSizePool : zabbixParams.size();//当使用cpu算出的线程数小于分页或未分页的数据条数时，使用cpu，否者使用数据条数
            ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSizePool, zabbixParams.size(), 60, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
            List<Future<Object>> futureList = new ArrayList<>();
            List<ZabbixProcessorParam> list = new ArrayList<>();
            //数据参数处理
            for (QueryMonitorValue zabbixParam : zabbixParams) {
                ZabbixProcessorParam zabbixProcessorParam = new ZabbixProcessorParam();
                MwTPServerTable mwTPServerTable = new MwTPServerTable();
                mwTPServerTable.setMonitoringServerType(zabbixParam.getMonitorType());
                //设置处理的监控对象（zabbix or Prometheus）
                MonitorProcessor processor = MonitorProcessor.build(mwTPServerTable, null);
                MonitorParamContext context = MonitorParamContext.build();
                context.setZabbixDubboService(zabbixDubboService);
                context.setQueryMonitorParam(zabbixParam);
                context.setParam(param);
                zabbixProcessorParam.setProcessor(processor);
                zabbixProcessorParam.setContext(context);
                list.add(zabbixProcessorParam);
            }
            //多线程
            for (ZabbixProcessorParam zabbixParam : list) {
                Callable<Object> callable = new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
                        MonitorParamContext context = zabbixParam.getContext();
                        MonitorProcessor processor = zabbixParam.getProcessor();
                        //传参数
                        Object[] arguments = {context};
                        //获取资产状态数据
                        Object result = method.invoke(processor, arguments);
                        return result;
                    }
                };
                Future<Object> submit = executorService.submit(callable);
                futureList.add(submit);
            }
            if (futureList.size() > 0) {
                futureList.forEach(f -> {
                    try {
                        Object result = f.get(20, TimeUnit.SECONDS);
                        listInfo.add(result);
                    } catch (Throwable e) {
                        log.error("fail to getDataInfoBydeviceName:多线程等待数据返回失败cause:{}", e);
                    }
                });
            }
            executorService.shutdown();
            log.info("关闭线程池");
        }
        return listInfo;
    }


    /**
     * 指定分组下
     * 使用多线程处理指定监控方法
     *
     * @param queryMonitorParam zabbix查询基础数据
     * @param param 多线程方法通用传参
     * @param methodName 方法名称
     * @return
     * @throws Exception
     */
    public List<Object> handlerSubListMethodThread(QueryMonitorParam queryMonitorParam, ParamContext param, String methodName) throws Exception {
        List<Object> listInfo = new ArrayList();
        if (param!=null && CollectionUtils.isNotEmpty(param.getSubList())) {
            List subList = param.getSubList();
            //反射获取MonitorProcessor对象
            Class<MonitorProcessor> monitorProcessorClass = MonitorProcessor.class;
            //设置指定方法的参数对象
            Class<?>[] parameterType = {MonitorParamContext.class};
            //获取方法
            Method method = monitorProcessorClass.getMethod(methodName, parameterType);
            //使用线程处理多个server服务
            int coreSizePool = Runtime.getRuntime().availableProcessors() * 2 + 1;
            coreSizePool = (coreSizePool < subList.size()) ? coreSizePool : subList.size();//当使用cpu算出的线程数小于分页或未分页的数据条数时，使用cpu，否者使用数据条数
            ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSizePool, subList.size(), 60, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
            List<Future<Object>> futureList = new ArrayList<>();
            log.info("多线程启用::"+subList);
            List<ParamContext> paramList = new ArrayList<>();
            for (Object obj : subList) {
                ParamContext paramContext = new ParamContext();
                BeanUtil.copyProperties(param, paramContext, CopyOptions.create().setIgnoreNullValue(true));
                paramContext.setObj(obj);
                paramList.add(paramContext);
            }
            //多线程
            for (ParamContext paramContext : paramList) {
                Callable<Object> callable = new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
                        log.info("多线程进来了！！！");
                        MwTPServerTable mwTPServerTable = new MwTPServerTable();
                        mwTPServerTable.setMonitoringServerType(queryMonitorParam.getMonitorType());
                        //设置处理的监控对象（zabbix or Prometheus）
                        MonitorProcessor processor = MonitorProcessor.build(mwTPServerTable, null);
                        MonitorParamContext context = MonitorParamContext.build();
                        context.setZabbixDubboService(zabbixDubboService);
                        context.setQueryMonitorParam(queryMonitorParam);
                        context.setParam(paramContext);
                        //传参数
                        Object[] arguments = {context};
                        //获取资产状态数据
                        Object result = method.invoke(processor, arguments);
                        return result;
                    }
                };
                Future<Object> submit = executorService.submit(callable);
                futureList.add(submit);
                log.info("futureList::"+futureList);
            }
            if (futureList.size() > 0) {
                futureList.forEach(f -> {
                    try {
                        Object result = f.get(60, TimeUnit.SECONDS);
                        listInfo.add(result);
                    } catch (Exception e) {
                        log.error("fail to getDataInfoBydeviceName:多线程等待数据返回失败cause:{}", e);
                    }
                });
            }
            executorService.shutdown();
            log.info("关闭线程池");
        }
        return listInfo;
    }


}
